﻿namespace MemoryTest_Communications.Model
{
    using System;
    using System.Device.Location;
    using System.Linq.Expressions;
    using global::Serialization;
    using WP7Contrib.Common;
    using WP7Contrib.Common.Helpers;

    /// <summary>
    /// The property.
    /// </summary>
    public sealed class Property : BaseModel, IEquatable<Property>, ICloneable<Property>
    {
        #region Constants and Fields

        public static readonly Property PlaceHolder = new Property { Guid = "29d6d1b7-54ba-4e80-ae82-282638e1ba7e", IsPlaceHolder = true };

        /// <summary>
        /// The auction date.
        /// </summary>
        private string auctionDate;

        /// <summary>
        /// The bathroom count.
        /// </summary>
        private int? bathroomCount;

        /// <summary>
        /// The bedroom count.
        /// </summary>
        private int? bedroomCount;

        /// <summary>
        /// The car spaces.
        /// </summary>
        private int? carSpaces;

        /// <summary>
        /// The commission.
        /// </summary>
        private string commission;

        /// <summary>
        /// The construction year.
        /// </summary>
        private int? constructionYear;

        /// <summary>
        /// The datasource name.
        /// </summary>
        private string datasourceName;

        /// <summary>
        /// The guid.
        /// </summary>
        private string guid;

        /// <summary>
        /// The guide price max.
        /// </summary>
        private decimal guidePriceMax;

        /// <summary>
        /// The guide price min.
        /// </summary>
        private decimal guidePriceMin;

        /// <summary>
        /// The image height.
        /// </summary>
        private int imageHeight;

        /// <summary>
        /// The image url.
        /// </summary>
        private string imageUrl;

        /// <summary>
        /// The image width.
        /// </summary>
        private int imageWidth;

        /// <summary>
        /// The keywords.
        /// </summary>
        private string keywords;

        /// <summary>
        /// The land size.
        /// </summary>
        private decimal? landSize;

        /// <summary>
        /// The land size uom.
        /// </summary>
        private string landSizeUom;

        /// <summary>
        /// The latitude.
        /// </summary>
        private decimal latitude;

        /// <summary>
        /// The lister name.
        /// </summary>
        private string listerName;

        /// <summary>
        /// The lister url.
        /// </summary>
        private string listerUrl;

        /// <summary>
        /// The listing type.
        /// </summary>
        private ListingType listingType;

        /// <summary>
        /// The location accuracy.
        /// </summary>
        private int locationAccuracy;
        
        /// <summary>
        /// The longitude.
        /// </summary>
        private decimal longitude;

        /// <summary>
        /// The price.
        /// </summary>
        private decimal price;

        /// <summary>
        /// The price currency.
        /// </summary>
        private string priceCurrency;

        /// <summary>
        /// The price formatted.
        /// </summary>
        private string priceFormatted;

        /// <summary>
        /// The price type.
        /// </summary>
        private PriceType priceType;

        /// <summary>
        /// The property type.
        /// </summary>
        private PropertyType propertyType;

        /// <summary>
        /// The room count.
        /// </summary>
        private int? roomCount;

        /// <summary>
        /// The summary.
        /// </summary>
        private string summary;

        /// <summary>
        /// The thumb height.
        /// </summary>
        private int? thumbHeight;

        /// <summary>
        /// The thumb url.
        /// </summary>
        private string thumbUrl;

        /// <summary>
        /// The thumb width.
        /// </summary>
        private int? thumbWidth;

        /// <summary>
        /// The title.
        /// </summary>
        private string title;

        /// <summary>
        /// The updated in days.
        /// </summary>
        private double updatedInDays;

        /// <summary>
        /// The updated in days formatted.
        /// </summary>
        private string updatedInDaysFormatted;

        #endregion

        public Property()
        {
            listingType = ListingType.Undefined;
            propertyType = PropertyType.Undefined;
            priceType = PriceType.Undefined;
        }

        public Property ShallowClone()
        {
            return CloneImpl();
        }

        public Property DeepClone()
        {
            return CloneImpl();
        }
        
        #region Properties

        /// <summary>
        /// Gets or sets IsPlaceHolder
        /// </summary>
        public bool IsPlaceHolder { get; private set; }

        /// <summary>
        /// Gets or sets BathroomCount.
        /// </summary>
        public int? BathroomCount
        {
            get
            {
                return this.bathroomCount;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.bathroomCount, value, () => this.BathroomCount);
            }
        }

        /// <summary>
        /// Gets or sets BedroomCount.
        /// </summary>
        public int? BedroomCount
        {
            get
            {
                return this.bedroomCount;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.bedroomCount, value, () => this.BedroomCount);
            }
        }

        /// <summary>
        /// Gets or sets DatasourceName.
        /// </summary>
        public string DatasourceName
        {
            get
            {
                return this.datasourceName;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.datasourceName, value, () => this.DatasourceName);
            }
        }

        /// <summary>
        /// Gets GeoCoordinate.
        /// </summary>
        public GeoCoordinate GeoCoordinate
        {
            get
            {
                return new GeoCoordinate(decimal.ToDouble(this.Latitude), decimal.ToDouble(this.Longitude));
            }
        }

        /// <summary>
        /// Gets or sets Guid.
        /// </summary>
        public string Guid
        {
            get
            {
                return this.guid;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.guid, value, () => this.Guid);
            }
        }

        /// <summary>
        /// Gets or sets GuidePriceMax.
        /// </summary>
        public decimal GuidePriceMax
        {
            get
            {
                return this.guidePriceMax;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.guidePriceMax, value, () => this.GuidePriceMax);
            }
        }

        /// <summary>
        /// Gets or sets GuidePriceMin.
        /// </summary>
        public decimal GuidePriceMin
        {
            get
            {
                return this.guidePriceMin;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.guidePriceMin, value, () => this.GuidePriceMin);
            }
        }

        /// <summary>
        /// Gets or sets ImageUrl.
        /// </summary>
        public string ImageUrl
        {
            get
            {
                return this.imageUrl;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.imageUrl, value, () => this.ImageUrl);
            }
        }

        /// <summary>
        /// Gets or sets Latitude.
        /// </summary>
        public decimal Latitude
        {
            get
            {
                return this.latitude;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.latitude, value, () => this.Latitude);
            }
        }

        /// <summary>
        /// Gets or sets ListerName.
        /// </summary>
        public string ListerName
        {
            get
            {
                return this.listerName;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.listerName, value, () => this.ListerName);
            }
        }

        /// <summary>
        /// Gets or sets ListerUrl.
        /// </summary>
        public string ListerUrl
        {
            get
            {
                return this.listerUrl;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.listerUrl, value, () => this.ListerUrl);
            }
        }

        /// <summary>
        /// Gets or sets ListingType.
        /// </summary>
        public ListingType ListingType
        {
            get
            {
                return this.listingType;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.listingType, value, () => this.ListingType);
            }
        }

        /// <summary>
        /// Gets or sets Longitude.
        /// </summary>
        public decimal Longitude
        {
            get
            {
                return this.longitude;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.longitude, value, () => this.Longitude);
            }
        }

         /// <summary>
        /// Gets or sets Location Accuracy.
        /// </summary>
        public int LocationAccuracy
        {
            get
            {
                return this.locationAccuracy;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.locationAccuracy, value, () => this.LocationAccuracy);
            }
        }

        /// <summary>
        /// Gets or sets Price.
        /// </summary>
        public decimal Price
        {
            get
            {
                return this.price;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.price, value, () => this.Price);
            }
        }

        /// <summary>
        /// Gets or sets PriceCurrency.
        /// </summary>
        public string PriceCurrency
        {
            get
            {
                return this.priceCurrency;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.priceCurrency, value, () => this.PriceCurrency);
            }
        }

        /// <summary>
        /// Gets or sets PriceFormatted.
        /// </summary>
        public string PriceFormatted
        {
            get
            {
                return this.priceFormatted;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.priceFormatted, value, () => this.PriceFormatted);
            }
        }

        /// <summary>
        /// Gets or sets PriceType.
        /// </summary>
        public PriceType PriceType
        {
            get
            {
                return this.priceType;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.priceType, value, () => this.PriceType);
            }
        }

        /// <summary>
        /// Gets or sets PropertyType.
        /// </summary>
        public PropertyType PropertyType
        {
            get
            {
                return this.propertyType;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.propertyType, value, () => this.PropertyType);
            }
        }

        /// <summary>
        /// Gets or sets RoomCount.
        /// </summary>
        public int? RoomCount
        {
            get
            {
                return this.roomCount;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.roomCount, value, () => this.RoomCount);
            }
        }

        /// <summary>
        /// Gets or sets Summary.
        /// </summary>
        public string Summary
        {
            get
            {
                return this.summary;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.summary, value, () => this.Summary);
            }
        }

        /// <summary>
        /// Gets or sets Title.
        /// </summary>
        public string Title
        {
            get
            {
                return this.title;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.title, value, () => this.Title);
            }
        }

        /// <summary>
        /// Gets or sets UpdatedInDays.
        /// </summary>
        public double UpdatedInDays
        {
            get
            {
                return this.updatedInDays;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.updatedInDays, value, () => this.UpdatedInDays);

                var days = ConversionHelper.ToInt32(value);
                this.LastUpdated = DateTime.Now.Date.AddDays(-1 * days);
                RaisePropertyChanged(() => LastUpdated);
            }
        }

        /// <summary>
        /// Gets or sets UpdatedInDaysFormatted.
        /// </summary>
        public string UpdatedInDaysFormatted
        {
            get
            {
                return this.updatedInDaysFormatted;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.updatedInDaysFormatted, value, () => this.UpdatedInDaysFormatted);
            }
        }

        public DateTime? LastUpdated { get; private set; }

        #endregion

        public static bool operator ==(Property p1, Property p2)
        {
            return Equals(p1, p2);
        }

        public static bool operator !=(Property p1, Property p2)
        {
            return !Equals(p1, p2);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }

            return obj is Property && this.Equals((Property)obj);
        }

        public override int GetHashCode()
        {
            return this.CombineHashCodes(new object[] {this.guid});
        }
        
        public bool Equals(Property property)
        {
            if (ReferenceEquals(null, property))
            {
                return false;
            }

            if (this.Guid != property.Guid)
            {
                return false;
            }
            
            return true;
        }

        private Property CloneImpl()
        {
            var clone = new Property
            {
                BathroomCount = this.BathroomCount,
                BedroomCount = this.BedroomCount,
                DatasourceName = this.DatasourceName,
                Guid = this.Guid,
                GuidePriceMax = this.GuidePriceMax,
                GuidePriceMin = this.GuidePriceMin,
                ImageUrl = this.ImageUrl,
                Latitude = this.Latitude,
                ListerName = this.ListerName,
                ListerUrl = this.ListerUrl,
                ListingType = this.ListingType,
                LocationAccuracy = this.LocationAccuracy,
                Longitude = this.Longitude,
                Price = this.Price,
                PriceCurrency = this.PriceCurrency,
                PriceFormatted = this.PriceFormatted,
                PriceType = this.PriceType,
                PropertyType = this.PropertyType,
                RoomCount = this.RoomCount,
                Summary = this.Summary,
                Title = this.Title,
                UpdatedInDays = this.UpdatedInDays,
                UpdatedInDaysFormatted = this.UpdatedInDaysFormatted,
                IsPlaceHolder = this.IsPlaceHolder
            };

            return clone;
        }

        protected override void SetPropertyAndNotify<T>(ref T currentValue, T newValue, Expression<Func<T>> propertyExpression)
        {
            if (IsPlaceHolder)
                return;
            
            base.SetPropertyAndNotify(ref currentValue, newValue, propertyExpression);
        }
    }
}